﻿using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerUI;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.AntiForgery;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Auditing;
using Volo.Abp.Autofac;
using Volo.Abp.Json;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Swashbuckle;
using Xms.Abp.Core;
using Xms.Abp.Modularity;

namespace Xms.Abp;

[DependsOn(
    typeof(AbpAutofacModule),
    typeof(AbpAspNetCoreMvcModule),
    typeof(AbpJsonModule),
    //typeof(AbpAspNetCoreMultiTenancyModule),
    typeof(AbpAspNetCoreSerilogModule),
    typeof(AbpSwashbuckleModule)
    )]
public abstract class XmsAbpStartupModule : XmsModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        base.ConfigureServices(context);

        context.Services.AddHttpContextAccessor();

        //Configure<DbContextOptions>(options =>
        //{
        //    options.WithExtension(new CoreOptionsExtension().WithSensitiveDataLoggingEnabled(true));
        //});

        //Configure<AbpDbContextOptions>(options =>
        //{
        //}); 

        Configure<AbpAntiForgeryOptions>(options =>
        {
            options.AutoValidate = false;
        });
        Configure<AbpMultiTenancyOptions>(options =>
        {
            options.IsEnabled = false;
        });
        Configure<AbpAuditingOptions>(options =>
        {
            options.IsEnabled = false; //Disables the auditing system
        });

        //Configure<AbpBackgroundJobOptions>(options => options.IsJobExecutionEnabled = false);

        context.Services.AddControllers();

        static string GetTypeName(Type type)
        {
            if (type.IsGenericType)
            {
                return $"{type.Name}<{type.GetGenericArguments().Select(r => GetTypeName(r)).JoinAsString(", ")}>";
            }
            else
            {
                return type.Name;
            }
        }
        context.Services.AddSwaggerGen(options =>
        {
            options.SwaggerDoc("management", new OpenApiInfo { Title = "后台管理接口文档", Version = "v1" });
            options.SwaggerDoc("open", new OpenApiInfo { Title = "对外接口文档", Version = "v1" });
            //options.DocInclusionPredicate((docName, description) => true);
            options.DocInclusionPredicate((docName, description) =>
            {
                return description.GroupName == docName || description.GroupName != "open" && docName == "management";
            });
            options.CustomSchemaIds(GetTypeName);
            options.EnableAnnotations();
        });
        Configure<SwaggerOptions>(options =>
        {
            options.RouteTemplate = $"/api/doc/{{documentName}}.json";
        });
        //Configure<SwaggerUIOptions>(options =>
        //{
        //    options.RoutePrefix = "api/doc";
        //    //var groupTypes = Enum.GetValues<ApiGroupType>();
        //    //var abp = new UrlDescriptor
        //    //{
        //    //    Name = "ABP 基础数据接口",
        //    //    Url = $"/{options.RoutePrefix}/abp-v1.0.json"
        //    //};
        //    //var list = groupTypes.Select(r => new UrlDescriptor
        //    //{
        //    //    Name = r.GetDescription() + " API v1.0",
        //    //    Url = $"/{options.RoutePrefix}/{r.ToString().ToKebabCase()}-v1.0.json"
        //    //}).ToList();
        //    //list.Add(abp);
        //    //options.ConfigObject.Urls = list;
        //});

        //context.Services.AddIdentity<EmcsUserEntity, EmcsUserRoleEntity>();

        //context.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        context.Services.AddAuthentication(IdentityConstants.ApplicationScheme)
               .AddCookie(IdentityConstants.ApplicationScheme, o =>
               {
                   o.LoginPath = new PathString("/login.html");
                   o.AccessDeniedPath = new PathString("/login.html");
               })
               .AddCookie(IdentityConstants.ExternalScheme, o =>
               {
                   o.Cookie.Name = IdentityConstants.ExternalScheme;
                   o.ExpireTimeSpan = TimeSpan.FromMinutes(5.0);
               })
               .AddCookie(IdentityConstants.TwoFactorRememberMeScheme, o =>
               {
                   o.Cookie.Name = IdentityConstants.TwoFactorRememberMeScheme;
                   var events1 = new CookieAuthenticationEvents
                   {
                       OnValidatePrincipal = new Func<CookieValidatePrincipalContext, Task>(SecurityStampValidator.ValidateAsync<ITwoFactorSecurityStampValidator>)
                   };
                   o.Events = events1;
               })
               .AddCookie(IdentityConstants.TwoFactorUserIdScheme, o =>
               {
                   o.Cookie.Name = IdentityConstants.TwoFactorUserIdScheme;
                   o.ExpireTimeSpan = TimeSpan.FromMinutes(5.0);
               });
        //.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o =>
        //{
        //    o.LoginPath = new PathString("/login.html");
        //    o.AccessDeniedPath = new PathString("/login.html");
        //});

        Configure<AbpAspNetCoreMvcOptions>(options =>
        {
            options.ConventionalControllers.Create(this.GetType().Assembly);
        });

        Configure<WebSocketOptions>(options =>
        {
            options.KeepAliveInterval = TimeSpan.FromSeconds(120);
        });
        Configure<SnowflakeSequenceOptions>(options =>
        {
            options.WorkerId = 1;
        });
        context.Services.AddMvcCore().AddApiExplorer();
    }

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        base.OnApplicationInitialization(context);

        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();
        var config = context.GetConfiguration();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseWhen(ctx => config["App:Urls"].Contains("https"), builder =>
        {
            builder.UseHttpsRedirection();
        });

        app.Use(async (ctx, next) =>
        {
            if (!ctx.Request.Path.Value.StartsWith("/api/")
                && config.GetSection("App").GetValue<bool>("HasSinglePageWeb"))
            {
                var path = Path.Combine(AppContext.BaseDirectory, "wwwroot", "index.html");
                if (!File.Exists(path))
                {
                    if (ctx.Request.Path.Value == "/")
                    {
                        ctx.Response.Redirect("/api/doc/index.html");
                    }
                    else
                    {
                        await next();
                    }
                }
                else if (ctx.Request.Path.Value.StartsWith("/api/doc/ui/abp."))
                {
                    ctx.Request.Path = ctx.Request.Path.Value.Replace("/api/doc/", "/swagger/");
                    await next();
                }
                else
                {
                    var text = await File.ReadAllTextAsync(path);
                    await ctx.Response.WriteAsync(text, Encoding.UTF8);
                }
            }
            else
            {
                await next();
            }
        });

        app.UseStaticFiles();

        //app.UseSerilogRequestLogging(options =>
        //{
        //    var action = options.EnrichDiagnosticContext;
        //    options.EnrichDiagnosticContext = (d, h) =>
        //    {
        //        d.Set("RequestId", h.TraceIdentifier);
        //        action?.Invoke(d, h);
        //    };
        //});

        app.UseAbpExceptionHandling();

        app.UseCors("Default");

        app.UseCorrelationId();

        //app.UseVirtualFiles();

        app.UseRouting();

        app.UseAuthentication();

        app.UseAuthorization();

        app.UseSwagger();
        //app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Eranntex.EMCS.Web v1"));
        app.UseAbpSwaggerUI(options =>
        {
            options.DocumentTitle = "文档中心";
            options.RoutePrefix = "api/doc";
            options.HeadContent = options.HeadContent.Replace("ui/abp", "/swagger/ui/abp");
            options.SwaggerEndpoint($"/{options.RoutePrefix}/open.json", "对外接口文档");
            options.SwaggerEndpoint($"/{options.RoutePrefix}/management.json", "后台管理接口文档");
        });

        app.UseAbpSerilogEnrichers();

        //https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/websockets?view=aspnetcore-5.0
        //https://blog.csdn.net/u011127019/article/details/91582041
        //app.UseWebSockets();

        app.UseConfiguredEndpoints();

        //app.UseEndpoints(endpoints =>
        //{
        //    endpoints.MapControllers();
        //});
    }
}

